#include "test_scheme.h"

using namespace std;
using namespace chrono;
void TestScheme::TestEncodeSingle()
{
    cout << "!!! Test ENCODE SINGLE !!!" << endl;
    long logN = 16;
    long L = 18;
    long logp = 36;

    TimeUtils timeUtils;
    long K = 6;
    Context context(logN, logp, L, K, 8, 6);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);
    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    complex<double> m = EvaluatorUtils::RandomCircle();
    //    complex<double> m = complex<double>(0, 1);

    timeUtils.Start("Encrypt single");
    Plaintext plaintext = scheme.EncodeSingle(m, L);
    timeUtils.Stop("Encrypt single");

    timeUtils.Start("Decrypt single");
    complex<double> d = scheme.DecodeSingle(plaintext);
    timeUtils.Stop("Decrypt single");

    StringUtils::ShowCompare(m, d, "val");

    cout << "!!! END TEST ENCODE SINGLE !!!" << endl;
}

void TestScheme::TestEncodeBatch(long logN, long L, long K, long logp, long T, int gamma, long logSlots)
{
    cout << "!!! Test ENCODE BATCH !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    // long k = L;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);
    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = 1L << logSlots;
    complex<double> *mvec = EvaluatorUtils::RandomComplexArray(slots);

    timeUtils.Start("Encrypt batch");
    Ciphertext cipher = scheme.Encrypt(mvec, slots, L);
    timeUtils.Stop("Encrypt batch");

    timeUtils.Start("Decrypt");
    complex<double> *dvec = scheme.Decrypt(secretKey, cipher);
    timeUtils.Stop("Decrypt");

    StringUtils::ShowCompare(mvec, dvec, slots, "val");

    cout << "!!! END TEST ENCODE BATCH !!!" << endl;
}

void TestScheme::TestEncryptSingle(long logN, long L, long logp)
{
    cout << "!!! Test Encrypt SINGLE !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    long k = 1;
    Context context(logN, logp, L, k, 8, 6);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);
    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    complex<double> m = EvaluatorUtils::RandomCircle();

    timeUtils.Start("Encrypt single");
    Ciphertext cipher = scheme.EncryptSingle(m, L);
    timeUtils.Stop("Encrypt single");

    timeUtils.Start("Decrypt single");
    complex<double> d = scheme.DecryptSingle(secretKey, cipher);
    timeUtils.Stop("Decrypt single");

    StringUtils::ShowCompare(m, d, "val");

    cout << "!!! END TEST Encrypt SINGLE !!!" << endl;
}

void TestScheme::TestEncrypt(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test Encrypt !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    timeUtils.Start("CreateSk");
    SecretKey secretKey(context);
    timeUtils.Stop("CreateSk");

    Scheme scheme(secretKey, context);

    timeUtils.Start("CreatePk");
    scheme.AddEncKey(secretKey);
    timeUtils.Stop("CreatePk");

    timeUtils.Start("CreateRelin");
    scheme.AddMultKey(secretKey);
    timeUtils.Stop("CreateRelin");

    timeUtils.Start("CreateGalois");
    scheme.AddLeftRotKey(secretKey, 1);
    timeUtils.Stop("CreateGalois");

    long slots = 1L << logSlots;
    complex<double> *mvec = EvaluatorUtils::RandomComplexArray(slots);

    Plaintext msg = scheme.Encode(mvec, slots, L);

    timeUtils.Start("EncryptSk");
    Ciphertext cipherSk = scheme.EncryptMsg(secretKey, msg);
    timeUtils.Stop("EncryptSk");
    timeUtils.Start("Decrypt");
    msg = scheme.DecryptMsg(secretKey, cipherSk);
    timeUtils.Stop("Decrypt");
    complex<double> *dvec = scheme.Decode(msg, cipherSk.scale);
    StringUtils::ShowCompare(mvec, dvec, 1, "EncryptSk");

    timeUtils.Start("EncryptPublic");
    Ciphertext cipherPk = scheme.EncryptMsg(msg);
    timeUtils.Stop("EncryptPublic");
    msg = scheme.DecryptMsg(secretKey, cipherPk);
    dvec = scheme.Decode(msg, cipherPk.scale);
    StringUtils::ShowCompare(mvec, dvec, 1, "EncryptPublic");
}

void TestScheme::TestBasic(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test BASIC !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    timeUtils.Start("私钥生成");
    SecretKey secretKey(context);
    timeUtils.Stop("私钥生成");
    Scheme scheme(secretKey, context);

    scheme.AddConjKey(secretKey);
    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);
    double bound = 1.0;
    double num = 2;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvec2 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *cvec = EvaluatorUtils::RandomComplexArray(slots, bound);

    complex<double> *mvecAdd = new complex<double>[slots];
    complex<double> *mvecCAdd = new complex<double>[slots];
    complex<double> *mvecMult = new complex<double>[slots];
    complex<double> *mvecCMult = new complex<double>[slots];

    for (long i = 0; i < slots; i++) {
        mvecAdd[i] = mvec1[i] + mvec2[i];
        mvecCAdd[i] = mvec1[i] + num;
        mvecMult[i] = mvec1[i] * mvec2[i];
        mvecCMult[i] = mvec1[i] * cvec[i];
    }
    timeUtils.Start("EncryptPublic");
    Ciphertext cipher1 = scheme.Encrypt(mvec1, slots, L);
    timeUtils.Stop("EncryptPublic");
    timeUtils.Start("EncryptPublic");
    Ciphertext cipher2 = scheme.Encrypt(mvec2, slots, L);
    timeUtils.Stop("EncryptPublic");

    Ciphertext addCipher = Ciphertext(context.N, slots, L, context.p);
    timeUtils.Start("Homomorphic Addition");
    scheme.Add(addCipher, cipher1, cipher2);
    timeUtils.Stop("Homomorphic Addition");

    timeUtils.Start("Homomorphic AdditionConst");
    Ciphertext addConstCipher = cipher1;
    scheme.AddConstAndEqual(addConstCipher, num);
    timeUtils.Stop("Homomorphic AdditionConst");

    Ciphertext multCipher = Ciphertext(context.N, slots, L, context.p);
    timeUtils.Start("Homomorphic Multiplication");
    scheme.Mult(multCipher, cipher1, cipher2);
    timeUtils.Stop("Homomorphic Multiplication");

    timeUtils.Start("ReScale");
    scheme.ReScaleByAndEqual(multCipher, 1);
    timeUtils.Stop("ReScale");


    timeUtils.Start("Decrypt and Decode");
    complex<double>* dvecAdd = scheme.Decrypt(secretKey, addCipher);
    timeUtils.Stop("Decrypt and Decode");
    complex<double> *dvecCAdd = scheme.Decrypt(secretKey, addConstCipher);
    timeUtils.Start("Decrypt and Decode");
    complex<double> *dvecMult = scheme.Decrypt(secretKey, multCipher);
    timeUtils.Stop("Decrypt and Decode");
    StringUtils::ShowCompare(mvecAdd, dvecAdd, 4, "Add");
    StringUtils::ShowCompare(mvecCAdd, dvecCAdd, 4, "AddConst");

    StringUtils::ShowCompare(mvecMult, dvecMult, 4, "mult");
}

void TestScheme::TestMultCt(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test MulCt !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);

    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);
    double bound = 1.0;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvec2 = EvaluatorUtils::RandomComplexArray(slots, bound);

    complex<double> *mvecMult = new complex<double>[slots];

    for (long i = 0; i < slots; i++) {
        mvecMult[i] = mvec1[i] * mvec2[i];
    }

    Ciphertext cipher1 = scheme.Encrypt(mvec1, slots, L);
    Ciphertext cipher2 = scheme.Encrypt(mvec2, slots, L);
    Ciphertext multCipher = Ciphertext(context.N, slots, L, context.p);
    timeUtils.Start("EvaluateMulCt+relin");
    scheme.Mult(multCipher, cipher1, cipher2);
    timeUtils.Stop("EvaluateMulCt+relin");

    timeUtils.Start("EvaluateMulCt+relin");
    scheme.MultAndEqual(cipher1, cipher2);
    timeUtils.Stop("EvaluateMulCt+relin");

    timeUtils.Start("EvaluateRescaleInplace");
    scheme.ReScaleByAndEqual(multCipher, 1);
    timeUtils.Stop("EvaluateRescaleInplace");

    timeUtils.Start("EvaluateRescaleInplace");
    scheme.ReScaleByAndEqual(cipher1, 1);
    timeUtils.Stop("EvaluateRescaleInplace");
    

    complex<double> *dvecMult = scheme.Decrypt(secretKey, multCipher);
    complex<double> *dvecMult1 = scheme.Decrypt(secretKey, cipher1);
    StringUtils::ShowCompare(mvecMult, dvecMult, 1, "mulCt");
    StringUtils::ShowCompare(mvecMult, dvecMult1, 1, "mulCt1");
}

void TestScheme::TestMultRepeat(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test MULT !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);

    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);
    double bound = 1.0;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvec2 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvecMult = new complex<double>[slots];
    int times = 16;
    for (long i = 0; i < slots; i++) {
        mvecMult[i] = mvec1[i] * mvec2[i];
        for (int j = 0; j < times; j++) {
            mvecMult[i] *= mvec2[i];
        }
    }
    timeUtils.Start("EncryptPublic Two");
    Ciphertext cipher1 = scheme.Encrypt(mvec1, slots, L);
    Ciphertext cipher2 = scheme.Encrypt(mvec2, slots, L);
    timeUtils.Stop("EncryptPublic Two");

    Ciphertext multCipher = Ciphertext(context.N, slots, L, context.p);
    clock_t start, end;

    timeUtils.Start("Homomorphic Multiplication & Rescaling");
    scheme.Mult(multCipher, cipher1, cipher2);
    scheme.ReScaleByAndEqual(multCipher, 1);
    timeUtils.Stop("Homomorphic Multiplication & Rescaling");

    // mult after rescale
    Ciphertext multCipher1;
    for (int i = 0; i < times; i++) {
        timeUtils.Start("Homomorphic Multiplication & Rescaling");
        scheme.Mult(multCipher, multCipher, cipher2);
        scheme.ReScaleByAndEqual(multCipher, 1);
        timeUtils.Stop("Homomorphic Multiplication & Rescaling");
    }

    timeUtils.Start("Decrypt");
    complex<double> *dvecMult = scheme.Decrypt(secretKey, multCipher);
    timeUtils.Stop("Decrypt");

    StringUtils::ShowCompare(mvecMult, dvecMult, 4, "mult");
}

void TestScheme::TestMultPt(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test MultPt !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    timeUtils.Start("私钥生成");
    SecretKey secretKey(context);
    timeUtils.Stop("私钥生成");
    Scheme scheme(secretKey, context);

    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);
    double bound = 1.0;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvec2 = EvaluatorUtils::RandomComplexArray(slots, bound);

    complex<double> *mvecMult = new complex<double>[slots];

    for (long i = 0; i < slots; i++) {
        mvecMult[i] = mvec1[i] * mvec2[i];
    }
    Ciphertext cipher = scheme.Encrypt(mvec1, slots, L);
    Plaintext plaintext = scheme.Encode(mvec2, slots, L);

    Ciphertext multCipher = Ciphertext(context.N, context.slots, L, context.p);
    timeUtils.Start("EvaluateMulPt");
    scheme.MultPt(multCipher, cipher, plaintext);
    timeUtils.Stop("EvaluateMulPt");

    scheme.ReScaleByAndEqual(multCipher, 1);

    complex<double> *dvecMult = scheme.Decrypt(secretKey, multCipher);

    StringUtils::ShowCompare(mvecMult, dvecMult, 1, "multPt");
}

void TestScheme::TestAddPt(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test AddPt !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);

    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);
    double bound = 1.0;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvec2 = EvaluatorUtils::RandomComplexArray(slots, bound);

    complex<double> *mvecAdd = new complex<double>[slots];

    for (long i = 0; i < slots; i++) {
        mvecAdd[i] = mvec1[i] + mvec2[i];
    }
    Ciphertext cipher = scheme.Encrypt(mvec1, slots, L);
    Plaintext plaintext = scheme.Encode(mvec2, slots, L);

    Ciphertext addCipher = Ciphertext(context.N, context.slots, L, context.p);
    timeUtils.Start("EvaluateAddPt");
    scheme.AddPt(cipher, plaintext);
    timeUtils.Stop("EvaluateAddPt");

    complex<double> *dvecMult = scheme.Decrypt(secretKey, cipher);
    StringUtils::ShowCompare(mvecAdd, dvecMult, 1, "addPt");
}
void TestScheme::TestAddCt(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test AddCt !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);

    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);
    double bound = 1.0;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvec2 = EvaluatorUtils::RandomComplexArray(slots, bound);

    complex<double> *mvecMult = new complex<double>[slots];

    for (long i = 0; i < slots; i++) {
        mvecMult[i] = mvec1[i] + mvec2[i];
    }
    Ciphertext ciphertext1 = scheme.Encrypt(mvec1, slots, L);
    Ciphertext ciphertext2 = scheme.Encrypt(mvec2, slots, L);

    timeUtils.Start("EvaluateAddCt");
    Ciphertext addCipher = scheme.Add(ciphertext1, ciphertext2);
    timeUtils.Stop("EvaluateAddCt");

    complex<double> *dvecMult = scheme.Decrypt(secretKey, addCipher);
    StringUtils::ShowCompare(mvecMult, dvecMult, 1, "addCt");
}
void TestScheme::TestSquare(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test SQUARE !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);

    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);
    double bound = 1.0;
    complex<double> *mvec = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvecSq = new complex<double>[slots];

    for (long i = 0; i < slots; i++) {
        mvecSq[i] = mvec[i] * mvec[i];
    }

    timeUtils.Start("Encrypt batch");
    Ciphertext cipher = scheme.Encrypt(mvec, slots, L);
    timeUtils.Stop("Encrypt batch");
    
    Ciphertext squareCipher = Ciphertext(context.N, cipher.slots, cipher.l, cipher.scale);
    timeUtils.Start("Homomorphic Square");
    scheme.Square(squareCipher, cipher);
    timeUtils.Stop("Homomorphic Square");
    scheme.ReScaleByAndEqual(squareCipher, 1);

    timeUtils.Start("Decrypt");
    complex<double> *dvecSq = scheme.Decrypt(secretKey, squareCipher);
    timeUtils.Stop("Decrypt");

    StringUtils::ShowCompare(mvecSq, dvecSq, 4, "square");
}

void TestScheme::TestConjugateBatch(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test CONJUGATE BATCH !!!" << endl;
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);
    // -----------------------------------------
    scheme.AddConjKey(secretKey);
    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);
    complex<double> *mvec = EvaluatorUtils::RandomComplexArray(slots);
    complex<double> *mvecconj = new complex<double>[slots];
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots);
    complex<double> *mvecconj1 = new complex<double>[slots];
    for (long i = 0; i < slots; i++) {
        mvecconj[i] = conj(mvec[i]);
        mvecconj1[i] = conj(mvec1[i]);
    }

    Ciphertext cipher = scheme.Encrypt(mvec, slots, L);
    Ciphertext cipher1 = scheme.Encrypt(mvec1, slots, L);
    Ciphertext cconj = Ciphertext(context.N, slots, L, context.p);
    Ciphertext cconj1 = Ciphertext(context.N, slots, L, context.p);
    timeUtils.Start("Conjugate batch");
    scheme.Conjugate(cconj,cipher);
    scheme.Conjugate(cconj1, cipher1);
//    cconj1 = scheme.Conjugate(cipher1);

    timeUtils.Stop("Conjugate batch");

    complex<double> *dvecconj = scheme.Decrypt(secretKey, cconj);
    StringUtils::ShowCompare(mvecconj, dvecconj, 4, "conj");

    complex<double> *dvecconj1 = scheme.Decrypt(secretKey, cconj1);
    StringUtils::ShowCompare(mvecconj1, dvecconj1, 4, "conj1");
    cout << "!!! END TEST CONJUGATE BATCH !!!" << endl;
}

void TestScheme::TestimultBatch()
{
    cout << "!!! Test i MULTIPLICATION BATCH !!!" << endl;
    long logN = 15;
    long L = 18;
    long logp = 36;
    long logSlots = 14;
    TimeUtils timeUtils;
    long K = 6;
    long T = 8;
    int gamma = 6;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);
    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);
    complex<double> *mvec = EvaluatorUtils::RandomComplexArray(slots);
    complex<double> *imvec = new complex<double>[slots];
    complex<double> *mvec1 = new complex<double>[slots];
    for (long i = 0; i < slots; i++) {
        mvec1[i].real(mvec[i].imag());
        mvec1[i].imag(-mvec[i].real());
        imvec[i].real(0.0);
        imvec[i].imag(1.0);
    }
    complex<double> i = complex<double>(0, 1);
    Ciphertext cipher = scheme.Encrypt(mvec, slots, L);

    timeUtils.Start("Multiplication by i batch");
    cipher = scheme.DivByi(cipher);
    //    cipher = scheme.IMult(cipher);
    //    scheme.IMultAndEqual(cipher);
    //    scheme.ReScaleByAndEqual(cipher, 1);
    timeUtils.Stop("Multiplication by i batch");

    complex<double> *dvec = scheme.Decrypt(secretKey, cipher);
    StringUtils::ShowCompare(mvec1, dvec, 8, "IMult");
    cout << "!!! END TEST i MULTIPLICATION BATCH !!!" << endl;
}

void TestScheme::TestRotateByPo2Batch(long logN, long L, long K, long T, long logp, int gamma, long logRotSlots,
    long logSlots, bool isLeft)
{
    cout << "!!! Test ROTATE BY POWER OF 2 BATCH !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);
    scheme.AddLeftRotKeys(secretKey);
    scheme.AddRightRotKeys(secretKey);
    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);
    long rotSlots = (1 << logRotSlots);
    complex<double> *mvec = EvaluatorUtils::RandomComplexArray(slots);
    Ciphertext cipher = scheme.Encrypt(mvec, slots, L);

    if (isLeft) {
        timeUtils.Start("Left Rotate by power of 2 batch");
        scheme.LeftRotateByPo2AndEqual(cipher, logRotSlots);
        timeUtils.Stop("Left Rotate by power of 2 batch");
    } else {
        timeUtils.Start("Right Rotate by power of 2 batch");
        scheme.RightRotateByPo2AndEqual(cipher, logRotSlots);
        timeUtils.Stop("Right Rotate by power of 2 batch");
    }

    complex<double> *dvec = scheme.Decrypt(secretKey, cipher);

    if (isLeft) {
        EvaluatorUtils::LeftRotateAndEqual(mvec, slots, rotSlots);
    } else {
        EvaluatorUtils::RightRotateAndEqual(mvec, slots, rotSlots);
    }

    StringUtils::ShowCompare(mvec, dvec, slots, "rot");
    // -----------------------------------------
    cout << "!!! END TEST ROTATE BY POWER OF 2 BATCH !!!" << endl;
}

void TestScheme::TestRotateTime(long logN, long L, long K, long T, long logp, int gamma, long rotSlots, long logSlots,
    bool isLeft)
{
    cout << "!!! Test ROTATE !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    timeUtils.Start("私钥生成");
    SecretKey secretKey(context);
    timeUtils.Stop("私钥生成");
    Scheme scheme(secretKey, context);
    srand(time(nullptr));
    long slots = (1 << logSlots);
    complex<double> *mvec = EvaluatorUtils::RandomComplexArray(slots);
    timeUtils.Start("EncryptPublic");
    Ciphertext cipher = scheme.Encrypt(mvec, slots, L);
    timeUtils.Stop("EncryptPublic");
    complex<double> *dvec;
    if(isLeft){
        timeUtils.Start("旋转密钥生成");
        scheme.AddLeftRotKey(secretKey, rotSlots);
        timeUtils.Stop("旋转密钥生成");

        timeUtils.Start("旋转密钥生成");
        scheme.AddLeftRotKey(secretKey, rotSlots);
        timeUtils.Stop("旋转密钥生成");
        timeUtils.Start("Left rotate batch");
        scheme.LeftRotateAndEqual(cipher, rotSlots);
        timeUtils.Stop("Left rotate batch");
        dvec = scheme.Decrypt(secretKey, cipher);
        EvaluatorUtils::LeftRotateAndEqual(mvec, slots, rotSlots);
    }
    else {
        scheme.AddLeftRotKey(secretKey, context.Nh - rotSlots);
        timeUtils.Start("Right rotate batch");
        scheme.RightRotateAndEqual(cipher, rotSlots);
        timeUtils.Stop("Right rotate batch");
        dvec = scheme.Decrypt(secretKey, cipher);
        EvaluatorUtils::RightRotateAndEqual(mvec, slots, rotSlots);
    }

    StringUtils::ShowCompare(mvec, dvec, 4, "rot");

    cout << "!!! END TEST ROTATE BATCH !!!" << endl;
}

void TestScheme::TestRotate(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test ROTATE BATCH !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);
    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);

    int rotSlots = EvaluatorUtils::RandomReal(slots / 2);
    cout << "rotslot: " << rotSlots << endl;
    scheme.AddLeftRotKey(secretKey, rotSlots);
    complex<double> *mvec = EvaluatorUtils::RandomComplexArray(slots);
    Ciphertext cipher = scheme.Encrypt(mvec, slots, L);

    timeUtils.Start("Left rotate batch");
    scheme.LeftRotateAndEqualFast(cipher, rotSlots);
    timeUtils.Stop("Left rotate batch");

    complex<double> *dvec = scheme.Decrypt(secretKey, cipher);

    EvaluatorUtils::LeftRotateAndEqual(mvec, slots, rotSlots);

    StringUtils::ShowCompare(mvec, dvec, 1, "rotate");
}

void TestScheme::TestMult(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test Mult !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);
    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);
    double bound = 1.0;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvec2 = EvaluatorUtils::RandomComplexArray(slots, bound);

    complex<double> *mvecMult = new complex<double>[slots];

    for (long i = 0; i < slots; i++) {
        mvecMult[i] = mvec1[i] * mvec2[i];
    }

    timeUtils.Start("EncryptPublic Two");
    Ciphertext cipher1 = scheme.Encrypt(mvec1, slots, L);
    Ciphertext cipher2 = scheme.Encrypt(mvec2, slots, L);
    timeUtils.Stop("EncryptPublic Two");
    cipher1.Print();
    cipher2.Print();

    Ciphertext multCipher = Ciphertext(context.N, slots, L, context.p);
    timeUtils.Start("Homomorphic Multiplication & Rescaling");
    scheme.Mult(multCipher, cipher1, cipher2);
    multCipher.Print();
    scheme.ReScaleByAndEqual(multCipher, 1);
    multCipher.Print();
    timeUtils.Stop("Homomorphic Multiplication & Rescaling");

    timeUtils.Start("Decrypt");
    complex<double> *dvecMult = scheme.Decrypt(secretKey, multCipher);
    timeUtils.Stop("Decrypt");

    StringUtils::ShowCompare(mvecMult, dvecMult, 2, "mult");
}

void TestScheme::TestModDownToQ0AndEqual(long logN, long L, long logp, long logSlots)
{
    cout << "!!! Test ModDownToQ0AndEqual !!!" << endl;
    long K = L + 1;
    Context context(logN, logp, L, K, 8, 6);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);

    long slots = (1 << logSlots);
    double bound = 1.0;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);

    Ciphertext cipher = scheme.Encrypt(mvec1, slots, L);
    cipher.Print();
    scheme.ModDownToQ0AndEqual(cipher);
    cipher.Print();
    complex<double> *dvecMult = scheme.Decrypt(secretKey, cipher);
    StringUtils::ShowCompare(mvec1, dvecMult, slots, "TestModDownToQ0AndEqual");
}

void TestScheme::TestCtS(long logp)
{
    long logN = 12;
    long L = 18;
    logp = 36;
    long K = 6;
    Context context(logN, logp, L, K, 8, 6);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);

    EncodingMatrix encodingMatrix(secretKey, scheme);

    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------


    long N = 1 << logN;
    long slots = N / 2;

    double bound = 1.0;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvec2 = EvaluatorUtils::RandomComplexArray(slots, bound);
    //    vector<complex<double>> aaa(slots);
    for (int i = 0; i < slots; i++) {
        //        mvec1[i].real(1);
        //        mvec1[i].imag(0);
        //         aaa[i] = mvec1[i];
    }

    //    auto rotateTemp = encodingMatrix.Rotate(aaa, 2048, true);
    //
    //    cout<<"asdasdadsa "<<mvec1[0]<<endl;
    //    cout<<"awqeqweqeqe"<<rotateTemp[0]<<endl;
    // //
    // ////    mvec1[0].real(0.12);
    // //
    auto *resultMid = new uint64_t[(L + K) << scheme.context.logN]();
    Plaintext plaintext(resultMid, N, slots, L + K);
    scheme.context.EncodePQ(plaintext.mx, mvec2, scheme.context.p, slots, L, K);

    Ciphertext ct0 = scheme.Encrypt(mvec1, slots, L);

    scheme.MulScalarP(ct0.ax, ct0.ax);
    scheme.MulScalarP(ct0.bx, ct0.bx);

    auto *ax = new uint64_t[(L + K) << scheme.context.logN];
    auto *bx = new uint64_t[(L + K) << scheme.context.logN];

    auto *axQ = new uint64_t[L << scheme.context.logN];
    auto *bxQ = new uint64_t[L << scheme.context.logN];

    copy(ct0.ax, ct0.ax + (L << scheme.context.logN), ax + (K << scheme.context.logN));
    copy(ct0.bx, ct0.bx + (L << scheme.context.logN), bx + (K << scheme.context.logN));

    Ciphertext ctPQ = Ciphertext(ax, bx, ct0.N, ct0.slots, ct0.l, ct0.scale);

    //    Ciphertext result = encodingMatrix.EvalMultExt(ctPQ,plaintext);
    Ciphertext result = encodingMatrix.EvalMultExt(ctPQ, encodingMatrix.mU0hatTPreFFT[2][0]);

    //   Ciphertext result = ctPQ;

    //    ct0 = encodingMatrix.DecrptPQ(result);
    for (long index = 0; index < K; index++) {
        uint64_t *ai = result.ax + (index << scheme.context.logN);
        uint64_t *bi = result.bx + (index << scheme.context.logN);
        scheme.context.PiINTTAndEqual(ai, index);
        scheme.context.PiINTTAndEqual(bi, index);
    }

    for (long index = K; index < ct0.l + K; index++) {
        uint64_t *ai = result.ax + (index << scheme.context.logN);
        uint64_t *bi = result.bx + (index << scheme.context.logN);
        scheme.context.QiINTTAndEqual(ai, index - K);
        scheme.context.QiINTTAndEqual(bi, index - K);
    }

    // axQ = scheme.context.BaseConvertPQl2Ql(result.ax, ct0.l);
    // bxQ = scheme.context.BaseConvertPQl2Ql(result.bx, ct0.l);

    scheme.context.BaseConvertPQl2Ql(axQ, result.ax, ct0.l);
    scheme.context.BaseConvertPQl2Ql(bxQ, result.bx, ct0.l);

    scheme.context.NTT(ct0.ax, axQ, ct0.l);
    scheme.context.NTT(ct0.bx, bxQ, ct0.l);

    //    copy(result.ax+K*N,result.ax+K*N+L*N,ct0.ax);
    //    copy(result.bx_CAE+K*N,result.bx_CAE+K*N+L*N,ct0.bx_CAE);

    ct0.scale = result.scale;

    scheme.ReScaleByAndEqual(ct0, 1);
    //
    //
    // //    cout<<" m1 :"<<mvec1[0]<<endl;
    for (int i = 0; i < slots; i++) {
        //        mvec1[i].real(mvec1[i].real()*mvec1[i].real());
        //        mvec1[i].imag(mvec1[i].imag()*mvec1[i].imag());
        mvec1[i] = mvec1[i] * mvec2[i];
    }
    complex<double> *dvecEvalmod = scheme.Decrypt(secretKey, ct0);

    for (int i = 0; i < slots; i++) {
        if (abs(dvecEvalmod[i].real()) > 0.001 || abs(dvecEvalmod[i].imag()) > 0.001) {
            cout << "解密的值：" << i << " :" << dvecEvalmod[i] << endl;
        }
    }

    StringUtils::ShowCompare(mvec1, dvecEvalmod, 5, "TestCTPT_QP");

    cout << "db";
}
// 第一版evalmod
void TestScheme::TestEvalMod(long logN, long L, long K, long T, long logp, int gamma)
{
    cout << "!!! TestEvalMod !!!" << endl;
    TimeUtils timeUtils;
    long N = 1 << logN;
    long slots = N / 2;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);
    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    double bound = 1.0;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);
    for (int i = 0; i < slots; i++) {
        mvec1[i].real(mvec1[i].real() / 4);
        mvec1[i].imag(0);
    }
    mvec1[0] = complex<double>(0.015625, 0);
    mvec1[1] = complex<double>(-0.015625, 0);
    mvec1[2] = complex<double>(-0.0234375, 0);
    Ciphertext ct0 = scheme.Encrypt(mvec1, slots, L);
    complex<double> *cccc = scheme.Decrypt(secretKey, ct0);
    for (int i = 0; i < slots; i++) {
        cccc[i] = mvec1[i];
    }

    // bootstrapping 参数
    uint64_t q = 0x4001b00001;
    int levelStart = 16;
    __uint128_t scalingFactor = 1L << 36;
    SineType SineType = SineType::Cos2;
    double messageRatio = 4;
    int k = 10;
    int sineDeg = 31;
    int doubleAngle = 2;
    int arcSineDeg = 0;
    // ---------------结果测试------
    double chebyshev_cos[32];
    double chebyshev_basis[32];
    double result;
    double sqrt2pi = 0.63163504;
    // ============================

    EvalModLiteral evalModLiteral(q, levelStart, scalingFactor, SineType, messageRatio, k, sineDeg, doubleAngle,
        arcSineDeg);
    EvalModPoly evalModPoly = EvalModPoly::NewEvalModPolyFromLiteral(evalModLiteral); // 创建多项式

    EncodingMatrix encodingMatrix(secretKey, scheme);

    Bootstrapper bootstrapper(context, scheme, secretKey, evalModPoly, encodingMatrix);

    bootstrapper.EvalMod(ct0);

    cout << "      最后结果比较      " << endl;
    complex<double> *dvecEvalmod = scheme.Decrypt(secretKey, ct0);

    // ---------拟合正确性验证------------
    for (int i = 0; i < sineDeg; i++) {
        chebyshev_cos[i] = evalSineChebyshevCoeff[i].real();
    }
    for (int i = 0; i < 5; i++) {
        double res = 0;
        for (int j = 0; j < sineDeg; j++) {
            if (j == 0) {
                chebyshev_basis[j] = 1;
                res += chebyshev_basis[j] * chebyshev_cos[j];
            } else if (j == 1) {
                chebyshev_basis[j] = mvec1[i].real() - 0.025;
                res += chebyshev_basis[j] * chebyshev_cos[j];
            } else {
                chebyshev_basis[j] = 2 * chebyshev_basis[1] * chebyshev_basis[j - 1] - chebyshev_basis[j - 2];
                res += chebyshev_basis[j] * chebyshev_cos[j];
            }
        }

        for (int p = 0; p < doubleAngle; p++) {
            sqrt2pi = sqrt2pi * sqrt2pi;
            res = 2 * pow(res, 2) - sqrt2pi;
        }
        cccc[i] = res;
        sqrt2pi = 0.63163504;
    }
    // =====================

    for (int i = 0; i < 5; i++) {
        cout << "原始: " << mvec1[i] << endl;
    }
    StringUtils::ShowCompare(cccc, dvecEvalmod, 5, "TestEvalMod_PIcos");
}

void TestScheme::TestBootstrapping(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test TestBootstrapping !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    TimeUtils bootstrapping;
    Context context(logN, logp, L, K, T, gamma);
    timeUtils.Start("私钥生成");
    SecretKey secretKey(context);
    timeUtils.Stop("私钥生成");
    Scheme scheme(secretKey, context);
    scheme.AddConjKey(secretKey);
    EvalModPoly evalModPoly;
    EncodingMatrix encodingMatrix(secretKey, scheme);
    Bootstrapper bootstrapper(context, scheme, secretKey, evalModPoly, encodingMatrix);
    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    std::vector<double> coefficients; // chebyshev系数
    long slots = (1 << logSlots);
    coefficients = gCoefficientsSparse;

    cout << "encoding!!!!" << endl;
    // ============================

    double bound = 1.0;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvec2 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvecMult = new complex<double>[slots];
    complex<double> *mvecMult1 = new complex<double>[slots];

    for (long i = 0; i < slots; i++) {
        mvecMult[i] = mvec1[i] * mvec2[i];
        mvecMult1[i] = mvecMult[i] * mvecMult[i];
    }
    for (int i = 0; i < slots; i++) {
        double ab = double(i % 10);
        mvec1[i].real(ab / 10);
        mvec1[i].imag(0);
    }

    timeUtils.Start("EncryptPublic Two");
    Ciphertext cipher1 = scheme.Encrypt(mvec1, slots, L);
    Ciphertext cipher2 = scheme.Encrypt(mvec2, slots, L);
    timeUtils.Stop("EncryptPublic Two");
    Ciphertext multCipher = Ciphertext(context.N, slots, L, context.p);
    scheme.Mult(multCipher, cipher1, cipher2);
    scheme.ReScaleByAndEqual(multCipher, 1);

    timeUtils.Start("Decrypt");
    complex<double> *dvec1 = scheme.Decrypt(secretKey, cipher1);
    timeUtils.Stop("Decrypt");

    multCipher = scheme.Encrypt(mvec1, slots, 2);// 只加密两层

    Ciphertext ct = multCipher;

    double smaller = 0.00195313;
    if (context.N == 32768)
    {
        smaller = 0.015625;
    }

    ct = scheme.MultByConst(ct, smaller); // 最开始除以512进行缩放
    scheme.ReScaleByAndEqual(ct, 1);

    bootstrapping.Start("bootstrapping");

    // step 1------------ModUpToQ-----------
    timeUtils.Start("ModUpToQAndEqual");

    scheme.ModUpToQAndEqual(ct);

    timeUtils.Stop("ModUpToQAndEqual");

    // step 2------------SubSum-----------
    timeUtils.Start("SubSum");
    // 由于我们进行满槽加密，所以没有SubSum
    timeUtils.Stop("SubSum");

    // step 3----------CoeffsToSlots--------
    timeUtils.Start("CoeffsToSlots");

    double pre = 0.5;
    double k = 1;
    double N = 1 << logN;
    double constantEvalMult = pre * (1.0 / (k * N));

    ct = scheme.MultByConst(ct, constantEvalMult); // 对密文进行缩放
    scheme.ReScaleByAndEqual(ct, 1);               // 密文L = 28--27密文

    // complex<double> *vec1 = scheme.Decrypt(secretKey, ct);

    Ciphertext ctxtEnc = encodingMatrix.EvalCoeffsToSlots(encodingMatrix.mU0hatTPreFFT, ct); // 密文L = 27-->24

    Ciphertext conj = Ciphertext(ctxtEnc.N, ctxtEnc.slots, ctxtEnc.l, ctxtEnc.scale);
    scheme.Conjugate(conj, ctxtEnc);

    Ciphertext ctxtEncI = scheme.Sub(ctxtEnc, conj);

    ctxtEncI = scheme.DivByi(ctxtEncI);

    ctxtEnc = scheme.Add(ctxtEnc, conj);

    timeUtils.Stop("CoeffsToSlots");

    // step 4------------EvalMod------------
    timeUtils.Start("EvalMod");

    Ciphertext ctxtEnc2 = bootstrapper.EvalChebyshevSeriesPS(ctxtEnc, coefficients, -1, 1);
    bootstrapper.ApplyDoubleAngleIterations(ctxtEnc2, 3);

    Ciphertext ctxtEncI2 = bootstrapper.EvalChebyshevSeriesPS(ctxtEncI, coefficients, -1, 1);
    bootstrapper.ApplyDoubleAngleIterations(ctxtEncI2, 3);

    timeUtils.Stop("EvalMod");

    // step 5----------SlotsToCoeffs--------
    timeUtils.Start("SlotsToCoeffs");

    ctxtEncI2 = scheme.MultByi(ctxtEncI2);

    Ciphertext CtSTC = scheme.Add(ctxtEnc2, ctxtEncI2);

    scheme.MulScalaInt(CtSTC, 2);

    CtSTC = encodingMatrix.EvalSlotsToCoeffs(encodingMatrix.mU0PreFFT, CtSTC); // 密文L = 15-->12

    scheme.ReScaleByAndEqual(CtSTC, 1);

    timeUtils.Stop("SlotsToCoeffs");

    double bigger = 512;
    if (context.N == 32768)
    {
        bigger = 64;
    }

    scheme.MulScalaInt(CtSTC, bigger); // 对密文进行缩放
    complex<double> *dvecMult2 = scheme.Decrypt(secretKey, CtSTC);
    StringUtils::ShowCompare(mvec1, dvecMult2, 10, "Bootstrapping");

    bootstrapping.Stop("bootstrapping");
}

void TestScheme::TestModUpToQ(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! TestModUpToQ !!!" << endl;

    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);

    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------
    long slots = (1 << logSlots);
    double bound = 1.0;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvec2 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvecMult = new complex<double>[slots];
    complex<double> *mvecMult1 = new complex<double>[slots];

    for (long i = 0; i < slots; i++) {
        mvecMult[i] = mvec1[i] * mvec2[i];
        mvecMult1[i] = mvecMult[i] * mvecMult[i];
    }

    timeUtils.Start("EncryptPublic Two");
    Ciphertext cipher1 = scheme.Encrypt(mvec1, slots, L);
    Ciphertext cipher2 = scheme.Encrypt(mvec2, slots, L);
    timeUtils.Stop("EncryptPublic Two");

    complex<double> *dvecMult123 = scheme.Decrypt(secretKey, cipher1);
    complex<double> *dvecMult1123 = scheme.Decrypt(secretKey, cipher2);

    Ciphertext multCipher = Ciphertext(context.N, slots, L, context.p);
    timeUtils.Start("Homomorphic Multiplication & Rescaling");
    scheme.Mult(multCipher, cipher1, cipher2);
    scheme.ReScaleByAndEqual(multCipher, 1);

    Ciphertext multCipher1 = scheme.Square(multCipher);
    scheme.ReScaleByAndEqual(multCipher1, 1);
    timeUtils.Stop("Homomorphic Multiplication & Rescaling");

    //    multCipher1.Print();

    timeUtils.Start("Decrypt");
    complex<double> *dvecMult = scheme.Decrypt(secretKey, multCipher);
    complex<double> *dvecMult1 = scheme.Decrypt(secretKey, multCipher1);
    timeUtils.Stop("Decrypt");

    StringUtils::ShowCompare(mvecMult, dvecMult, 5, "mult");
    StringUtils::ShowCompare(mvecMult1, dvecMult1, 5, "mult");

    timeUtils.Start("ModUpToQAndEqual");
    scheme.ModUpToQAndEqual(multCipher1);
    timeUtils.Stop("ModUpToQAndEqual");

    complex<double> *dvecMult2 = scheme.Decrypt(secretKey, multCipher1);
    StringUtils::ShowCompare(mvecMult1, dvecMult2, 5, "ModUpToQAndEqual");
}

void TestScheme::TestNegMult()
{
    cout << "!!! Test Mult !!!" << endl;
    long logN = 16;
    long L = 18;
    long logp = 36;
    long logSlots = 1;
    long slots = 1 << 15;
    long K = 6;
    Context context(logN, logp, L, K, 8, 6);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);

    srand(0);

    complex<double> mvec1 = { 1, 1 };
    complex<double> mvec2 = { 1, 1 };
    complex<double> *mvec3 = new complex<double>[16384];
    complex<double> *mvec4 = new complex<double>[16384];
    complex<double> *mvec5 = new complex<double>[16384];
    for (int i = 0; i < slots; i++) {
        mvec3[i] = complex<double>(-5, 0);
        mvec4[i] = complex<double>(-5, 0);
        mvec5[i] = mvec3[i] * mvec4[i];
    }

    complex<double> mvecMult;

    mvecMult = mvec1 * mvec2;

    Ciphertext cipher1 = scheme.EncryptSingle(mvec1, L);
    Ciphertext cipher2 = scheme.EncryptSingle(mvec2, L);
    Ciphertext cipher3 = scheme.Encrypt(mvec3, slots, L);
    Ciphertext cipher4 = scheme.Encrypt(mvec4, slots, L);
    
    Ciphertext multCipher = Ciphertext(context.N, slots, L, context.p);
    scheme.Mult(multCipher, cipher1, cipher2);
    scheme.ReScaleByAndEqual(multCipher, 1);

    Ciphertext multCipher1 = Ciphertext(context.N, slots, L, context.p);
    scheme.Mult(multCipher1, cipher3, cipher4);
    scheme.ReScaleByAndEqual(multCipher1, 1);

    complex<double> *dvecMult = scheme.Decrypt(secretKey, multCipher);
    complex<double> *dvecMult1 = scheme.Decrypt(secretKey, multCipher1);

    std::cout << "Expected: " << mvecMult << std::endl;
    std::cout << "Output: " << *dvecMult << std::endl;

    StringUtils::ShowCompare(mvec5, dvecMult1, 8, "neg");
}

void TestScheme::TestMultConst()
{
    cout << "!!! START TestMultConst !!!" << endl;
    long logN = 12;
    long L = 28;
    long logp = 53;
    long K = 4;
    long slots = 1 << (logN - 1);
    Context context(logN, logp, L, K, 8, 4);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);
    srand(0);
    //    complex<double> m1 = {5, 0};
    double m1 = 512;
    //    complex<double>* mvec2 = new complex<double>[slots];
    complex<double> *mvec3 = new complex<double>[slots];
    complex<double> *mvec2 = EvaluatorUtils::RandomComplexArray(32768);
    for (int i = 0; i < slots; i++) {
        //        mvec2[i] = complex<double>(-3, -1);
        mvec3[i] = mvec2[i] * m1;
    }
    Ciphertext cipher1 = scheme.Encrypt(mvec2, slots, L);
    Ciphertext multCipher = scheme.MultByConst(cipher1, m1);
    scheme.MultByConstAndEqual(cipher1, m1);
    scheme.ReScaleByAndEqual(multCipher, 1);
    complex<double> *dvecMult = scheme.Decrypt(secretKey, multCipher);

    StringUtils::ShowCompare(mvec3, dvecMult, 8, "TestMultConst");
}

void TestScheme::TestFHEMod(long logN, long L, long K, long T, long logp, int gamma, long logSlots)
{
    cout << "!!! Test FHEMod !!!" << endl;
    // -----------------------------------------
    TimeUtils timeUtils;
    Context context(logN, logp, L, K, T, gamma);
    SecretKey secretKey(context);
    Scheme scheme(secretKey, context);
    scheme.AddConjKey(secretKey);
    EvalModPoly evalModPoly;
    EncodingMatrix encodingMatrix(secretKey, scheme);
    Bootstrapper bootstrapper(context, scheme, secretKey, evalModPoly, encodingMatrix);
    // -----------------------------------------
    srand(time(nullptr));
    // -----------------------------------------

    std::vector<double> coefficients; // chebyshev系数

    long slots = (1 << logSlots);
    double bound = 1.0;
    complex<double> *mvec1 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *mvec2 = EvaluatorUtils::RandomComplexArray(slots, bound);
    complex<double> *cvec = EvaluatorUtils::RandomComplexArray(slots, bound);

    for (int i = 0; i < slots; i++) {
        mvec1[i].real(0);
        mvec1[i].imag(0);
    }
    mvec1[0].real(0.1);
    mvec1[1].real(0.2);
    mvec1[2].real(0.3);
    mvec1[3].real(0.4);
    mvec1[4].real(0.5);
    mvec1[5].real(0.6);
    mvec1[6].real(0.7);
    mvec1[7].real(0.8);

    complex<double> *mvecAdd = new complex<double>[slots];
    complex<double> *mvecMult = new complex<double>[slots];
    complex<double> *mvecCMult = new complex<double>[slots];
    Ciphertext ctxtEnc = scheme.Encrypt(mvec1, slots, 28);

    for (int i = 0; i < 4; i++) {
        ctxtEnc = scheme.MultByConst(ctxtEnc, 1);
        scheme.ReScaleByAndEqual(ctxtEnc, 1);
    }

    coefficients = gCoefficientsSparse;

    complex<double> *dvec = scheme.Decrypt(secretKey, ctxtEnc);
    StringUtils::ShowCompare(mvec1, dvec, 1, "原密文");
    timeUtils.Start("切比雪夫");

    Ciphertext ctxt = bootstrapper.EvalChebyshevSeriesPS(ctxtEnc, coefficients, -1, 1);
    timeUtils.Stop("切比雪夫");

    bootstrapper.ApplyDoubleAngleIterations(ctxt, 3);

    complex<double> *dvecEval = scheme.Decrypt(secretKey, ctxt);

    for (int i = 0; i < 1; i++) {
        cout << "evalmod结果：" << dvecEval[i] << endl;
    }

    StringUtils::ShowCompare(mvec1, dvecEval, 1, "eval最后密文");
}
